#include "chemnitz_pool.h"

// Make splint happy
/*@-incondefs@*/
int ftime (/*@out@*/ struct timeb *tp);
/*@+incondefs@*/

CHEMNITZ_POOL chemnitz_pool_open (uint8_t *key)
{
	CHEMNITZ_POOL pool;
	struct timeb t;
	pool = (CHEMNITZ_POOL) malloc (sizeof (*pool));
	if (pool)
	{
		(void) ftime (&t);
		memcpy (pool->key, key, 16);
		memset (pool->state, 0, BYTES_PER_BLOCK);
		pool->is_open = true;
		pool->millitm = (uint16_t) t.millitm;
		return pool;
	}
	else 
	{
		return 0;
	}
}

bool chemnitz_pool_is_open (const CHEMNITZ_POOL pool)
{
	if (pool)
	{
		return pool->is_open;
	}
	else
	{
		return false;
	}
}

void chemnitz_pool_put (CHEMNITZ_POOL pool, const uint64_t entropy)
{
	struct timeb t;
	uint64_t *l;
	uint8_t buf [BYTES_PER_BLOCK];
	(void) ftime (&t);
	l = (uint64_t *) pool->state;
	*l = *l ^ entropy;
	*l = *l ^ (uint64_t) (t.millitm + pool->millitm);
	pool->millitm = (uint16_t) t.millitm;
	chemnitz_encrypt (pool->state, pool->key, buf);
	memcpy (pool->state, buf, BYTES_PER_BLOCK);
}

uint64_t chemnitz_pool_get (CHEMNITZ_POOL pool)
{
	uint64_t rval;
	struct timeb t;
	uint64_t *l;
	uint8_t buf [BYTES_PER_BLOCK];
	(void) ftime (&t);
	l = (uint64_t *) pool->state;
	rval = *l;
	*l = *l ^ (uint64_t) (t.millitm + pool->millitm);
	pool->millitm = (uint16_t) t.millitm;
	chemnitz_encrypt (pool->state, pool->key, buf);
	memcpy (pool->state, buf, BYTES_PER_BLOCK);
	return rval;
}

int chemnitz_pool_serialize (CHEMNITZ_POOL pool, const char *path)
{
	int fd;
	ssize_t c;
	fd = open ((char *) path, O_WRONLY | O_CREAT, 0600);
	if (fd < 0)
	{
		return -1;
	}
	else
	{
		c = write (fd, pool->state, BYTES_PER_BLOCK);
		(void) close (fd);
		return 0;
	}
}

int chemnitz_pool_deserialize (CHEMNITZ_POOL pool, const char *path)
{
	int fd;
	ssize_t c;
	fd = open ((char *) path, O_RDONLY);
	if (fd < 0)
	{
		return -1;
	}
	else
	{
		c = read (fd, pool->state, BYTES_PER_BLOCK);
		(void) close (fd);
		return 0;
	}
}

void chemnitz_pool_close (CHEMNITZ_POOL pool)
{
	if (pool)
	{
		free (pool);
	}
}

